﻿using LinqKit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Web;
using ZSoftInstagram.DataAccess.Abstractions;
using ZSoftInstagram.Domain;
using ZSoftInstagram.Domain.Addressing;
using ZSoftInstagram.Domain.Board;
using ZSoftInstagram.Domain.User;
using ZSoftInstagram.Services.Abstraction;
using ZSoftInstagram.Services.Upload;
using ZSoftInstagram.UI.ViewModels;
using System.Linq;
using System.Data.Entity;

namespace ZSoftInstagram.Services
{
    public class ImagePinService : IPinService
    {
        private IRepository<ImagePin> _repository;
        private UploadService _uploadService;

        public IRepository<ImagePin> Repository
        {
            get
            {
                return this._repository;
            }
        }

        public ImagePinService(IRepository<ImagePin> repository, UploadService uploadService)
        {
            this._repository = repository;
            this._uploadService = uploadService;
        }

        public Task<int> CreateAsync(PinCreateModel model)
        {
            var board = _repository.GetTypeRepository<ImagePinBoard>().First(b => b.Id == model.BoardId, "Pins");
            var pin = new ImagePin()
            {
                Date = DateTime.Now,
                Description = model.Description,
                Address = new Address() { Path = _uploadService.Upload(model.File), AddresType = AddressTypes.FileSystem },
                Tags = this.AddTags(model.RawTags.Split(',')).Result,
                PinBoards = new List<ImagePinBoard>() { board }
            };

            this._repository.Add(pin);
            return this._repository.SaveChangesAsync();
        }

        private async Task<ICollection<Tag>> AddTags(string[] tags)
        {
            if (tags == null)
                return null;

            List<Tag> result = new List<Tag>();
            bool hasNew = false;

            foreach (var item in tags)
            {
                var tag = this._repository.GetTypeRepository<Tag>().FirstOrDefault(t => t.Text == item, null);
                if (tag == null)
                {
                    result.Add(new Tag() { Text = item });
                    hasNew = true;
                }
                else
                {
                    result.Add(tag);
                }
            }

            if (hasNew)
            {
                await this._repository.SaveChangesAsync();
            }

            return result;
        }

        public void DeletePin(PinDeleteModel pin)
        {
            throw new NotImplementedException();
        }

        public void PinToBoard(ApplicationUser pinner, PinBoard board, Pin item)
        {
            throw new NotImplementedException();
        }

        public void RemoveFromBoard(Pin pin)
        {
            throw new NotImplementedException();
        }

        public Task<int> DeleteAsync(PinDeleteModel model)
        {
            var pin = this.Repository.First(p => p.Id == model.Id, "Address");
            this._repository.JustDeleteAsync(p => p.Id == model.Id);

            try
            {
                File.Delete(HttpContext.Current.Server.MapPath(pin.Address.Path));
            }
            catch (Exception)
            {

            }


            return this.Repository.SaveChangesAsync();
        }

        public Task<int> AddLikeAsync(AddLikeModel model, int userId)
        {
            var pin = this._repository.First(l => l.Id == model.Id, "Likes");
            pin.Likes.Add(new Like() { Date = DateTime.Now, UserId = userId });

            return this._repository.SaveChangesAsync();
        }

        public Task<int> RepinAsync(RePinModel model, int userId)
        {
            var pin = this._repository.FirstOrDefault(p => p.Id == model.PinId);
            var boardsPredicate = PredicateBuilder.False<ImagePinBoard>();
            model.BoardIds.ForEach(item =>
            {
                boardsPredicate = boardsPredicate.Or(b => b.Id == item);
            });

            var boards = 
                (this._repository.GetTypeRepository<ImagePinBoard>().All() as DbSet<ImagePinBoard>)
                            .Include("Pins")
                                .AsExpandable()
                                    .Where(boardsPredicate).ToList();
            
            boards.ForEach(b => b.Pins.Add(pin));
            return this._repository.SaveChangesAsync();
        }

        public Task<List<PinViewModel>> GetByTagAsync(string[] tags)
        {
            var predicate = PredicateBuilder.False<ImagePin>();
            tags.ForEach(t => predicate = predicate.Or(tc => tc.Tags.Any(t1 => t1.Text == t)));

            return
                (this._repository.All() as DbSet<ImagePin>).AsExpandable().Where(predicate).Select(p => new PinViewModel()
                {
                    Id = p.Id,
                    Address = new AddressViewModel() { AddressType = p.Address.AddresType, Path = p.Address.Path }
                }).ToListAsync();
        }
    }
}
